﻿<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

	<head>
		<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
		<title>Entities - 实体</title>
		<link href="bootstrap.min.css" type="text/css" rel="stylesheet" />
	</head>

	<body>

		<div class="document-contents">

			<p>Entities are one of the core concepts of DDD (Domain Driven Design). Eric Evans describe it as "<em>An object that is not fundamentally defined by its attributes, but rather by a thread of continuity and identity</em>". 
			So, entities have Id's and stored in a database. An entity is 
			generally mapped to a table for relational databases.</p>
			<p class="translation">
				实体是 DDD（领域驱动设计）的核心概念之一。
				Eirc Evans 是这样描述它的：“<em>一个对象从根本上不是通过属性定义的，而是通过一连串的连续性事件和标识定义的</em>”。
				因此，实体都拥有 Id 并存储在数据库中。一个实体通常被映射到关系型数据库的一张表。
			</p>
			<h3 id="DocEntityClasses">Entity Class - 实体类</h3>
			<p>In ASP.NET Boilerplate, Entities are derived from <strong>Entity </strong>
class. See the sample below:</p>
			<p class="translation">在 ASP.NET Boilerplate 中，实体是派生自 <strong>Entity </strong>类。看下面的示例：</p>

			<pre lang="cs">public class Person : Entity
{
    public virtual string Name { get; set; }

    public virtual DateTime CreationTime { get; set; }

    public Person()
    {
        CreationTime = DateTime.Now;
    }
}</pre>

			<p>
				<strong>Person</strong> class is defined as an entity. It has two properties. 
Also, Entity class defines an <strong>Id</strong> property. It's <strong>primary 
key </strong>of the Entity. So, name of primary keys of all Entities are same, 
it's <strong>Id</strong>.</p>
			<p class="translation">
				<strong>Person</strong> 类已定义的实体。它拥有两个属性。同时 Entity 类定义了一个 <strong>Id</strong> 属性。
				它是实体的<strong>主键</strong>。因此，所有实体的主键名称是相同的，它叫 <strong>Id</strong>。
			</p>
			<p>Type of Id (primary key) can be changed. It's
				<strong>int </strong>(Int32) by default. If you want to define another 
type as Id, you should explicitly declare it as shown below:</p>
			<p class="translation">
				Id（主键）的类型是可以被修改的。它默认是 <strong>int </strong>（Int32）。
				如果你想为 Id 定义其它的类型，你应该像下面那样显示声明：
			</p>

			<pre lang="cs">public class Person : Entity&lt;long&gt;
{
    public virtual string Name { get; set; }

    public virtual DateTime CreationTime { get; set; }

    public Person()
    {
        CreationTime = DateTime.Now;
    }
}</pre>

			<p>Also, you can set it as string, Guid or something else.</p>
			<p class="translation">同样，你可以设置它为字符串，Guid 或其它类型。</p>
			<p>Entity class overrides <strong>equality</strong> operator (==) to easily 
check if two entities are equal (their Id is equal). It also defines the <strong>
IsTransient()</strong> method to check if it has an Id or not.</p>
			<p class="translation">
				Entity 类重写了<strong>相等</strong>运算符（==），可以轻松地检查两个实体是否相同（实体的 Id 相同则认为它们相同）。
				它也定义了 <strong>IsTransient()</strong> 方法来检测它是否拥有一个 Id。
			</p>
			<h3>AggregateRoot Class - AggregateRoot 类</h3>
			<p>"<em>Aggregate is a pattern in Domain-Driven Design. A DDD 
			aggregate is a cluster of domain objects that can be treated as a 
			single unit. An example may be an order and its line-items, these 
			will be separate objects, but it's useful to treat the order 
			(together with its line items) as a single aggregate.</em>" (Martin 
			Fowler - see
			<a href="http://martinfowler.com/bliki/DDD_Aggregate.html" target="_blank">
			full description</a>)</p>
			<p class="translation">
				"<em>聚合是领域驱动设计中的一种模式。一个 DDD 聚合是一组领域对象，它可以被作为单个单元进行处理。例如：订单和订单项，这些都是单独的对象。但是，我们可以将订单(以及订单项)作为一个聚合来进行处理。</em>"
				（Martin Fowler - 请参阅<a href="http://martinfowler.com/bliki/DDD_Aggregate.html" target="_blank">完整描述</a>）
			</p>
			<p>While ABP does not enforce you to use aggregates, you may want to 
			create aggregates and aggregate roots in your application. ABP 
			defines <strong>AggregateRoot</strong> class that extends Entity to 
			create aggregate root entities for an aggregate.</p>
			<p class="translation">
				虽然 ABP 不强制你使用聚合，你可能会想在你的应用中创建聚合以及聚合根。ABP 定义了一个派生自 Entity 的 <strong>AggregateRoot</strong> 类，它可以让你为聚合创建聚合根实体。
			</p>
			<h4>Domain Events - 领域事件</h4>
			<p>AggregateRoot defines <a href="EventBus-Domain-Events.html">
			<strong>DomainEvents</strong></a> collection to generate domain 
			events by the aggregate root class. These events are automatically 
			triggered just before the current <a href="Unit-Of-Work.html">unit 
			of work</a> is completed. Actually, any entity can generate domain 
			events by implementing <strong>IGeneratesDomainEvents</strong> 
			interface, but it's common (best practice) to generate domain events 
			in aggregate roots. That's why it's default for AggregateRoot but 
			not for Entity class.</p>
			<p class="translation">
				AggregateRoot 聚合根类定义了 <a href="EventBus-Domain-Events.html"><strong>DomainEvents</strong></a> 集合来生成领域事件。这些事件仅在当前<a href="Unit-Of-Work.html">工作单元</a>完成前自动触发。
				实际上，任何实体都可以通过实现 <strong>IGeneratesDomainEvents</strong> 接口来生成领域事件，但通常（最佳实践）在聚合根中生成领域事件。
				这就是为什么它被定义在 AggregateRoot 中而不是 Entity 类中。
			</p>
			<h3 id="DocConventionalInterfaces">Conventional Interfaces - 接口约定</h3>
			<p>In many application, similar entity properties (and database table fields) 
are used like CreationTime indicates that when this entity is created. ASP.NET 
Boilerplate provides some useful interfaces to make this common properties 
explicit and expressive. Also, this provides a way of coding common code for 
Entities which implement these interfaces.</p>
			<p class="translation">
				在很多应用程序中，一般都具有像 CreationTime 一样的实体属性（及数据库表字段）来指示该实体是什么时候被创建的。
				ASP.NET Boilerplate 提供了一些有用的接口来使用共通属性变得明确并富有表现力。
				此外，这也为实现了这些接口的实体类提供了一种编写通用代码的方式。
			</p>
			<h4 id="DocAuditing">Auditing - 审计</h4>
			<p>
				<strong>IHasCreationTime</strong> makes it possible to use a common property 
for '<strong>creation time</strong>' information of an entity. ASP.NET Boilerplate automatically sets 
CreationTime to <strong>current time</strong> when an Entity is inserted 
into database which implements this interface.</p>
			<p class="translation">
				<strong>IHasCreationTime</strong> 使得使用一个共通的属性来描述一个实体的‘<strong>创建时间</strong>’信息成为可能。
				当实现了这个接口的实体插入到数据库中时，ASP.NET Boilerplate 会自动地将<strong>当前时间</strong>设置给 CreationTime。
			</p>
			<pre lang="cs">public interface IHasCreationTime
{
    DateTime CreationTime { get; set; }
}</pre>
			<p>Person class can be re-written as shown below by implementing IHasCreationTime interface:</p>
			<p class="translation">Person 类可以通过重写来实现 IHasCreationTime 接口，如下所示：</p>
			<pre lang="cs">public class Person : Entity&lt;long&gt;, IHasCreationTime
{
    public virtual string Name { get; set; }

    public virtual DateTime CreationTime { get; set; }

    public Person()
    {
        CreationTime = DateTime.Now;
    }
}</pre>

			<p>
				<strong>ICreationAudited</strong> extens IHasCreationTime by adding&nbsp; 
				<strong>CreatorUserId</strong>:</p>
			<p class="translation"><strong>ICreationAudited</strong> 扩展了 IHasCreationTime，它新增了 <strong>CreatorUserId</strong>：</p>
			<pre lang="cs">public interface ICreationAudited : IHasCreationTime
{
    long? CreatorUserId { get; set; }
}</pre>
			<p>ASP.NET Boilerplate automatically sets CreatorUserId to <strong>current 
user's id</strong> when saving a new entity. You can also implement ICreationAudited easily by deriving your entity from
				<strong>CreationAuditedEntity</strong> class. It has also a generic version for 
different type of Id properties.</p>
            <p class="translation">
				当保存一个新实体时，ASP.NET Boilerplate 自动把<strong>当前用户 id</strong> 设置给 CreatorUserId。
				你也可以从 <strong>CreationAuditedEntity</strong> 类派生你的实体，从而更容易的实现 ICreationAudited。
				它还有一个泛型版本来适用于不同类的 Id 属性。
			</p>
			<p>There is also similar interfaces for modifications:</p>
			<p class="translation">还为修改提供了类似的接口：</p>
			<pre lang="cs">public interface IHasModificationTime
{
    DateTime? LastModificationTime { get; set; }
}

public interface IModificationAudited : IHasModificationTime
{
    long? LastModifierUserId { get; set; }
}</pre>
			<p>ASP.NET Boilerplate also automatically sets these properties when updating an 
entity. You just define them for your entity.</p>
			<p class="translation">ASP.NET Boilerplate 会在一个实体更新时自动设置这些属性。你只需要在你的实体中定义它们。</p>
			<p>If you want to implement all of audit properties, you can direcly implement
				<strong>IAudited </strong>interface:</p>
			<p class="translation">如果你想实现所有的审计属性，你可以直接实现 <strong>IAudited </strong>接口。</p>
			<pre lang="cs">public interface IAudited : ICreationAudited, IModificationAudited
{

}</pre>
			<p>As a shortcut, you can derive from <strong>AuditedEntity </strong>class 
instead of direcly implementing <strong>IAudited</strong>. AuditedEntity class has 
also a generic version for different type of Id properties.</p>
			<p class="translation">
				作为一个捷径，你可以从 <strong>AuditedEntity </strong> 类派生而不是直接实现 <strong>IAudited</strong>。
				AuditedEntity 类也为不同类型的 Id 属性提供了泛型版本。
			</p>
			<p>Note: ASP.NET Boilerplate gets current user's Id from
				<a href="/Pages/Documents/Abp-Session">ABP Session</a>.</p>
			<p class="translation">注意：ASP.NET Boilerplate gets 从 <a href="/Pages/Documents/Abp-Session">ABP 会话</a> 获取当前用户 Id。</p>
			<h4 id="DocSoftDelete">Soft Delete - 软删除</h4>
			<p>Soft delete is a commonly used pattern to mark an Entity as deleted instead 
of actually deleting it from database. For instace, you may not want to hard 
delete a User from database since it has many releations to other tables. 
				<strong>ISoftDelete</strong> interface is used for this purpose:</p>
			<p class="translation">
				软删除是一种通用模式，用于将实体标记为已删除而不是实际从数据库中删除它。
				例如：你可能不想从数据库中硬删除一个用户，因为它与许多其它的表有关联关系。
				<strong>ISoftDelete</strong> 接口被用于这个目的：
			</p>
			<pre lang="cs">public interface ISoftDelete
{
    bool IsDeleted { get; set; }
}</pre>
			<p>ASP.NET Boilerplate implements soft delete pattern out-of-the-box. When a 
soft-delete entity is being deleted, ASP.NET Boilerplate detects this, prevents 
deleting, sets IsDeleted as true and updates entity in the database. Also, it 
does not retrive (select) soft deleted entities from database, automatically 
filters them.</p>
			<p class="translation">
				ASP.NET Boilerplate 实现了开箱即用的软删除模式。
				当一个实现了软删除的实体正在被删除时，ASP.NET Boilerplate 会发现这个动作，并阻止其删除，设置 IsDeleted 属性值为 true 并且更新实现到数据库中。
				同时，它不会从数据库中取回（select）已被软删除的实体，自动过滤了它们。
			</p>
			<p>If you use soft delete, you may also want to store information when an entity 
is deleted and who deleted it. You can implement <strong>IDeletionAudited</strong> interface that is shown below:</p>
			<p class="translation">
				如果你使用了软删除，也有可能想去存储是谁删除了这个实体。你可以实现 <strong>IDeletionAudited</strong> 接口，如下所示：
			</p>
			<pre lang="cs">public interface IDeletionAudited : ISoftDelete
{
    long? DeleterUserId { get; set; }

    DateTime? DeletionTime { get; set; }
}
			</pre>
			<p>IDeletionAudited extends ISoftDelete as you noticed. ASP.NET Boilerplate 
automatically sets these properties when an entity is deleted.</p>
			<p class="translation">
				你会发现 IDeletionAudited 扩展了 ISoftDelete。ASP.NET Boilerplate 会在实体被删除时自动设置这些属性。
			</p>
			<p>If you want to implement all audit interfaces (creation, modification and 
deletion) for an entity, you can directly implement <strong>IFullAudited</strong> 
since it inherits all:</p>
			<p class="translation">
				如果你想为一个实体实现所有的审计接口（创建，修改及删除），你可以直接实现 IFullAudited，因为它继承了所有这些接口：
			</p>
			<pre lang="cs">public interface IFullAudited : IAudited, IDeletionAudited
{

}</pre>
			<p>As a shortcut, you can derive your entity from <strong>
FullAuditedEntity</strong> class that implements all.</p>
			<p class="translation">
				作为一个捷径，你可以从 <strong>FullAuditedEntity</strong> 类派生你的实体。
			</p>
			<ul>
				<li>NOTE 1: All audit interfaces and classes have a generic version for defining navigation property 
to your <strong>User</strong> entity (like ICreationAudited&lt;TUser&gt; and 
				FullAuditedEntity&lt;TPrimaryKey, TUser&gt;).</li>
				<p class="translation">
					注意 1：为了在你的 <strong>User</strong> 实体（像 ICreationAudited&lt;TUser&gt; 和 FullAuditedEntity&lt;TPrimaryKey, TUser&gt;）中定义导航属性，所有审计接口和类都拥有一个泛型版本。
				</p>
				<li>
				<p>NOTE 2: Also, all of them has an <strong>AggregateRoot</strong> 
				version, like AuditedAggregateRoot.</p>
				</li>
				<p class="translation">
					注意 2：同样，它们还拥有一个 <strong>AggregateRoot</strong> 版本，像 AuditedAggregateRoot。
				</p>
			</ul>
			<h4 id="DocActivePassive">Active/Passive Entities - 激活/未激活实体</h4>
			<p>Some entities need to be marked as Active or Passive. Then you may take 
action upon active/passive state of the entity. You can implement <strong>
IPassivable</strong> interface that is created for this reason. It defines
				<strong>IsActive</strong> property.</p>
			<p class="translation">
				一些实体需要被标记为激活或未激活。那么你可以在实体的激活/未激活状态上采取行动。
				基于这个原因而创建的实体，你可以实现 <strong>IPassivable</strong> 接口。它定义了 <strong>IsActive</strong> 属性。
			</p>
			<p>If your entity will be active on first creation, you can set IsActive to true 
in the constructor.</p>
			<p class="translation">
				如果你的实体将在第一次创建时被激活，那么你可在构造函数中设置 IsActive 为真。
			</p>
			<p>This is different than soft delete (IsDeleted). If an entity is soft deleted, 
it can not be retrieved from database (ABP prevents it as default). But, for 
active/passive entities, it's completely up to you to control getting entities.</p>
			<p class="translation">
				这不同于软删除（IsDeleted）。如果一个实体是被软删除的，那么它不能从数据库中取回（默认情况下 ABP 阻止了它）。
				但是，对于激活/未激活实体，完全由你来控制它们的获取。
			</p>
			<h3 id="DocIEntityInterfaces">Entity Change Events - 实体改变事件</h3>
			<p>ASP.NET Boilerplate automatically triggers certain events when an entity is 
inserted, updated or deleted. Thus, you can register to these events and perform 
any logic you need. See Predefined Events section in
				<a href="/Pages/Documents/EventBus-Domain-Events">event bus documentation</a> 
for more information.</p>
			<p class="translation">
				ASP.NET Boilerplate 在实体被插入、更新或删除时自动触发相应的事件。因此，你可注册这些事件并执行任何你需要的逻辑。
				更多信息请参阅<a href="/Pages/Documents/EventBus-Domain-Events">事件总线文档</a>中的预定义事件。
			</p>
			<h3>IEntity Interfaces - IEntity 接口</h3>
			<p>Actually, <strong>Entity </strong>class implements <strong>IEntity </strong>interface (and <strong>Entity&lt;TPrimaryKey&gt;</strong> implements <strong>IEntity&lt;TPrimaryKey&gt;</strong>). 
If you do not want to derive from Entity class, you can implement these 
interfaces directly. There are also corresponding interfaces for other entity 
classes. But this is not the suggested way, unless you have a good 
reason to do not derive from Entity classes.</p>
			<p class="translation">
				实际上，<strong>Entity </strong>类实现了 <strong>IEntity </strong>接口（及 <strong>Entity&lt;TPrimaryKey&gt;</strong> 实现了 <strong>IEntity&lt;TPrimaryKey&gt;</strong>）。
				如果你不想从 Entity od 派生，你可以直接实现这些接口。
				还有其它实体类相应的接口。但不建议使用这种方法，除非你能找到一个很好的理由不从 Entity 类派生。
			</p>
			<h3>IExtendableObject Interface - IExtendableObject 接口</h3>
			<p>ASP.NET Boilerplate provides a simple interface, 
			<strong>IExtendableObject</strong>, to easily associate <strong>arbitrary name-value data</strong> to 
			an entity. Consider this simple entity:</p>
			<p class="translation">
				ASP.NET Boilerplate 提供了一个简单的接口 <strong>IExtendableObject</strong>，它可轻松的关联<strong>任意名称-值数据</strong>到一个实体。
				思考这个简单的实体：
			</p>
			<pre lang="cs">public class Person : Entity, <strong>IExtendableObject</strong>
{
    public string Name { get; set; }

    <strong>public string ExtensionData { get; set; }</strong>

    public Person(string name)
    {
        Name = name;
    }
}</pre>
			<p><strong>IExtendableObject</strong> just defines <strong>ExtensionData</strong> 
			string property which is used to store <strong>JSON formatted</strong> name value 
			objects. Example:</p>
			<p class="translation">
				在 <strong>IExtendableObject</strong> 接口中仅定定义了一个字符串属性：<strong>ExtensionData</strong>，该属性用来存储 JSON 格式的名称-值对象。如下所示：
			</p>
			<pre lang="cs">var person = new Person("John");

<strong>person.SetData("RandomValue", RandomHelper.GetRandom(1, 1000));
person.SetData("CustomData", new MyCustomObject { Value1 = 42, Value2 = "forty-two" });</strong></pre>
			<p>We can use any type of object as value to <strong>SetData</strong> method. When we 
			use such the code above, <strong>ExtensionData</strong> will be like 
			that:</p>
			<p class="translation">
				我们可以使用 <strong>SetData</strong> 方法来设置任意类型的值。
				当我们运用了上面的代码时，ExtensionData 的值将会是：
			</p>
			<pre lang="js">{"CustomData":{"Value1":42,"Value2":"forty-two"},"RandomValue":178}</pre>
			<p>Then we can use <strong>GetData</strong> to get any value:</p>
			<p class="translation">然后，我们可以使用 <strong>GetData</strong> 来获取任意值：</p>
			<pre lang="cs">var randomValue = person.<strong>GetData&lt;int&gt;("RandomValue")</strong>;
var customData = person.<strong>GetData&lt;MyCustomObject&gt;("CustomData")</strong>;
</pre>
			<p>While this technique can be very useful in some cases (when you 
			need to provide ability to dynamically add extra data to an entity), 
			you normally should use regular properties. Such a dynamic usage is 
			not type safe and explicit.</p>
			<p class="translation">
				虽然在某些情况下（当你需要动态的添加额外数据到实体的时候），这个技术是非常有用。但在正常情况下应该使用正规的属性。
				像这样的动态用法是类型不安全且不明确的。
			</p>

		</div>

	</body>

</html>
